$(function () {
    const MAPPINGS = {
        '[Z':'boolean[]',
        '[B':'byte[]',
        '[S':'short[]',
        '[C':'char[]',
        '[I':'int[]',
        '[J':'long[]',
        '[F':'float[]',
        '[D':'double[]'
    }, cancel_select = function () {
        return false;
    };

    function patchTypeName (name) {
        if (name[0] !== '[') {
            return name;
        }

        if (name[1] === 'L') {
            return name.substring (2, name.length - 1) + '[]';
        }

        if (name[1] in MAPPINGS) {
            return MAPPINGS [name[1]];
        }
        throw "Unknown Type!";
    }

    function patchItems (srf) {
        srf.id = Math.random().toString ().substring (2);

        if ('items' in srf) {
            let item, i, j, k, method, parameter;
            for (k in srf.items) if (srf.items.hasOwnProperty (k)) {
                item = srf.items [k];

                if (('types' in item) && item.types.length) {
                    for (i = 0; i < item.types.length; i ++) {
                        item.types [i] = patchTypeName (item.types [i]);
                    }
                }

                if (('methods' in item) && item.methods.length) {
                    for (i = 0; i < item.methods.length; i ++) {
                        method = item.methods[i];
                        method.type = patchTypeName (method.type);

                        if (('parameters' in method) && method.parameters.length) {
                            for (j = 0; j < method.parameters.length; j ++) {
                                parameter = method.parameters[j];
                                parameter.type = patchTypeName (parameter.type);
                            }
                        }
                    }
                }
            }
        }

        return srf;
    }

    function onCreate (message) {
        if (!('data' in message) || !('frames' in message.data)) {
            console.warn ('empty data: ', message);
            return;
        }

        let frames = message.data.frames, k;
        for (k in frames) if (frames.hasOwnProperty (k)) {
            let srf = patchItems (frames[k]);
            build (srf);
        }
    }

    function onUpdate (message) {
        if (!'data' in message) {
            console.warn ('empty message: ', message);
            return;
        }

        patchItems (message.data);
        build (message.data);
    }

    function onWithdrawn (message) {
        if (!'data' in message) {
            console.warn ('empty message: ', message);
            return;
        }

        let srf = $('.srf-root[data-namespace="' + message.data.namespace + '"]');
        if (srf) {
            srf.removeData ('srf');
            srf.remove ();
        }
    }

    function build (srf) {
        let root = $('<ul class="srf-root"/>').attr ('data-namespace', srf.namespace).data ('srf', srf).appendTo ($(document.body)),
            title = $('<li/>')/*.on ('mousedown', on_mouse_down)*/.appendTo (root),
            update = new Date (srf.timestamp), li, k, item, ul;
        $('<div class="srf-title"/>').text (srf.namespace).appendTo (title);
        $('<div class="srf-title-meta"/>').html ('&lt;&lt;from: ' + srf.host + ':' + srf.port + '&gt;&gt;').appendTo (title);
        $('<div class="srf-title-meta"/>').html ('&lt;&lt;updated: ' + update.format () + '&gt;&gt;').appendTo (title);
        if ('items' in srf) for (k in srf.items) if (srf.items.hasOwnProperty (k)) {
            item = srf.items[k];
            li = $('<li/>').attr ('data-endpoint', item.name).on ('click', on_sri_clicked).appendTo (root);
            ul = $('<ul class="sri"/>').appendTo (li);
            $('<li/>').text (item.name).appendTo (ul);
            $('<li><i class="fa fa-fw fa-angle-right"></i></li>').appendTo (ul);
        }
    }

    function on_sri_clicked () {
        let self = $(this), root = self.closest ('.srf-root'), state = self.attr ('data-state'),
            srf = root.data ('srf'), endpoint = self.attr ('data-endpoint'),
            sri, icon = self.find ('li:last-child');

        if (state === 'expended') {
            if (icon) {
                $('<i class="fa fa-fw fa-angle-right"></i>').appendTo (icon.empty ());
            }

            mask.hide (function () {
                sri = root.data ('attached');
                if (sri) {
                    root.removeData ('attached').css ('z-index', 'auto');
                    self.removeAttr ('data-state');

                    sri.fadeOut (function () {
                        sri.remove ();
                    });
                }
            });
        } else {
            if (icon) {
                $('<i class="fa fa-fw fa-angle-left"></i>').appendTo (icon.empty ());
            }
            sri = create_sri (srf, endpoint).css ({
                // 'z-Index': 1000,
                left: root.position ().left + root.width () + 2,
                top: self.offset ().top
            }).hide();
            root.data ('attached', sri).css ('z-index', 2000);
            self.attr ('data-state', 'expended');
            mask.show (function () {
                sri.fadeIn ();
            });
        }
    }

    function create_sri (srf, endpoint) {
        let sri = srf.items [endpoint], root = $('<div class="sri-root" />').appendTo ($(document.body)),
            table, tr, td, content, i, j, m;
        $('<div class="sri-title"/>').text (sri.name).appendTo (root);
        $('<div class="sri-type">&lt;&lt;sri&gt;&gt;</div>').appendTo (root);
        $('<hr size="1"/>').appendTo (root);
        table = $('<table class="sri-items"/>').appendTo (root);
        tr = $('<tr/>').appendTo (table);
        $('<td>reference:</td>').appendTo (tr);
        td = $('<td/>').appendTo (tr);
        content = '';
        for (i = 0; i < sri.types.length; i ++) {
            if (i !== 0) content += '<br/>';
            content += sri.types [i];
        }
        td.html (content);
        $('<hr size="1"/>').appendTo (root);
        if (('methods' in sri) && sri.methods.length) {
            table = $('<table class="sri-items" />').appendTo (root);
            for (i = 0; i < sri.methods.length; i ++) {
                m = sri.methods [i];
                tr = $('<tr/>').appendTo (table);
                $('<td class="return-type" />').text (m.type).appendTo (tr);
                td = $('<td class="method-name"/>').appendTo (tr);
                content = m.name + ' (';
                for (j = 0; j < m.parameters.length; j ++) {
                    if (j !== 0) {
                        content += ', ';
                    }
                    content += m.parameters[j].type;
                }
                content += ')';
                td.text (content);
            }
        }
        return root;
    }

    if (!('format' in Date.prototype)) {
        Date.prototype.format = function () {
            let year = this.getFullYear(), month = this.getMonth() + 1, date = this.getDate(),
                hour = this.getHours(), minute = this.getMinutes(), second = this.getSeconds(), s = year + '-';

            if (month < 10) s += '0';
            s += month + '-';
            if (date < 10) s += '0';
            s += date + ' ';
            if (hour < 10) s += '0';
            s += hour + ':';
            if (minute < 10) s += '0';
            s += minute + ':';
            if (second < 10) s += '0';
            s += second;
            return s;
        }
    }

    $.ajax ({url:'backend/server-info'}).done (function (response) {
        socket.connect (response ['context-path'] + '/ws/monitor');
    });
    $('.sri').on ('click', on_sri_clicked);
    $('ul, li, div').on ('selectstart', cancel_select);

    let Mask = function () {
        let panel = $('.mask');
        if (!panel.length) {
            panel = $('<div class="mask"/>').css ({width: $(window).width(), height: $(window).height ()}).hide ().appendTo ($(document.body))
        }

        function M () {}
        M.prototype = {
            show : function (callback) {
                panel.show (callback);
/*
                panel.animate ({opacity: .5}, 'slow', true, function () {
                    panel.show ();
                })
*/
                // panel.show ();
            },
            hide : function (callback) {
                panel.hide (callback);
                // panel.hide ();
            }
        };
        return M;
    } (), mask = new Mask (), socket = new WebSocketWrapper ({
        onmessage : function (message) {
            message = JSON.parse (message);

            switch (message.action) {
                case 'create':
                    onCreate (message);
                    break;
                case 'update':
                    onUpdate (message);
                    break;
                case 'withdrawn':
                    onWithdrawn (message);
                    break;
                default :
                    console.warn ('unknown command');
            }
        }
    });
});